﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Media3D;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Windows.Controls.Primitives;
using Bling.Properties;
using Bling.DSL;
using Bling.Util;
using Bling.Animation;
using Bling.Linq;
using linq = Microsoft.Linq.Expressions;
 
namespace Bling.DSL {
  public abstract partial class Expr<T> : BasePropertyContainer {
    public PropertyValue<S> Property<S>(DependencyProperty Property) {
      return Property<S>(Property, Property);
    }
    public virtual PropertyValue<S> Property<S>(DependencyProperty ReadProperty, DependencyProperty WriteProperty) {
      return new DependencyPropertyValue<T,S>() {
        TargetValue = this, ReadProperty = ReadProperty, WriteProperty = WriteProperty,
      };
    }
    public PropertyValue<S> Property<S>(DependencyProperty[] Properties, IntBl index) {
      throw new NotSupportedException();
    }
    public void ForAll<S>(DependencyProperty[] Properties, Action<Expr<S>, IntBl> F) {
      throw new NotSupportedException();
    }
  }
  public partial class UpCastExpr<T, S> where T : S {
    public override PropertyValue<K> Property<K>(DependencyProperty ReadProperty, DependencyProperty WriteProperty) {
      return Underlying.Property<K>(ReadProperty, WriteProperty);
    }
  }

  public abstract partial class Brand<T, BRAND> where BRAND : Brand<T, BRAND> {
    /// <summary>
    /// Like Bind, except performs TwoWay databinding. 
    /// </summary>
    public BRAND TwoWay {
      get {
        return this;
      }
      set {
        if (!Underlying.Solve(new Properties.BindEval() { DoTwoWay = true }, value))
          throw new NotSupportedException();
      }
    }
    private class CommandX : Assign {
      public Action<DependencyObject, DependencyProperty> F;
      public bool Accept<S>(Expr<S> LHS, Expr<S> RHS) {
        if (!(LHS is DependencyPropertyValue<S>)) return false;
        var LHS0 = (DependencyPropertyValue<S>)LHS;
        F(LHS0.Target, LHS0.WriteProperty);
        return true;
      }
      public Assign Copy() { return this; }
    }
    /// <summary>
    /// Clear the current now assigned value of underlying properties.
    /// </summary>
    /// <remarks>WPF dependency property only.</remarks>
    public void ClearNow() {
      if (!Underlying.Solve(new CommandX() { F = (a,b) => a.ClearValue(b) }, new Constant<T>(default(T)))) throw new NotSupportedException();
    }
    /// <summary>
    /// Clear the current constraint of underlying properties.
    /// </summary>
    /// <remarks>WPF dependency property only.</remarks>
    public void ClearConstraint() {
      if (!Underlying.Solve(new CommandX() { F = (a, b) => BindingOperations.ClearBinding(a,b) }, new Constant<T>(default(T)))) throw new NotSupportedException();
    }
    /// <summary>
    /// Stop animation of the underlying dependency property.
    /// </summary>
    /// <param name="capture">Whether to capture the curent value as locally set.</param>
    /// <remarks>WPF dependency property only.</remarks>
    public void Stop(bool capture) {
      if (!Underlying.Solve(new CommandX() { F = (a, b) => {
        var v = a.GetValue(b);
        ((IAnimatable)a).BeginAnimation(b, null);
        if (capture) a.SetValue(b, v);
      } }, new Constant<T>(default(T)))) throw new NotSupportedException();
    }
    /// <summary>
    /// Clear all values, binding, and animation.
    /// </summary>
    /// <param name="capture">Whether to capture the curent value as locally set.</param>
    /// <remarks>WPF dependency property only.</remarks>
    public void ClearAll(bool capture) {
      if (!Underlying.Solve(new CommandX() { F = (a, b) => {
        var v = a.GetValue(b);
        a.ClearValue(b);
        BindingOperations.ClearBinding(a, b);
        if (a is IAnimatable) ((IAnimatable)a).BeginAnimation(b, null);
        if (capture) a.SetValue(b, v);
      } }, new Constant<T>(default(T)))) throw new NotSupportedException();
    }

    /// <summary>
    /// Access animation operations and options of underlying properties.
    /// </summary>
    /// <remarks>WPF dependency property only.</remarks>
    public Properties.Animate<T, BRAND> Animate {
      get { return new Bling.Properties.Animate<T, BRAND>() { Target = this }; }
    }
  }
  public class DynamicDictionary<K, E> : DependencyObject {
    public void Add(K key) { }
    public void Remove(K key) { }
  }


  /// <summary>
  /// A dependency object that will create a list of objects whose count can change dynamically. 
  /// </summary>
  /// <typeparam name="T">Type of object that is contained in list.</typeparam>
  public class DynamicArray<T> : DependencyObject {
    private static readonly GetProperty<DynamicArray<T>,int> CountProperty =
      "Count".NewProperty<DynamicArray<T>, int>(0, (x, oldV, newV) => x.CountChanged(oldV, newV));
    private readonly Func<int, T> AddT;
    private readonly Action<int, T> RemT;
    /// <summary>
    /// Number of objects to create. This is a dependency property. 
    /// </summary>
    public IntBl Count {
      get { return CountProperty[this]; }
      set { Count.Bind = value; }
    }
    /// <summary>
    /// Create a new dynamic array with specified add and remove functions.
    /// </summary>
    /// <param name="AddT">Function to call when object is added.</param>
    /// <param name="RemT">Function to call when object is removed.</param>
    public DynamicArray(Func<int, T> AddT, Action<int, T> RemT) {
      this.AddT = AddT; this.RemT = RemT;
    }
    private readonly List<T> Elements = new List<T>();
    /// <summary>
    /// Clear all objects from list. 
    /// </summary>
    public void Clear() {
      while (Elements.Count > 0) {
        var e = Elements[Elements.Count - 1];
        Elements.RemoveAt(Elements.Count - 1);
        RemT(Elements.Count, e);
      }
      Count = 0;
    }
    /// <summary>
    /// Get element by index.
    /// </summary>
    /// <param name="index">Index of element to access.</param>
    /// <returns>Accessed element.</returns>
    public T this[int index] {
      get { return Elements[index]; }
    }
    private void CountChanged(int oldV, int newV) {
      while (Elements.Count < newV)
        Elements.Add(AddT(Elements.Count));
      while (Elements.Count > newV) {
        var e = Elements[Elements.Count - 1];
        Elements.RemoveAt(Elements.Count - 1);
        RemT(Elements.Count, e);
      }
    }
  }
}

namespace Bling.Properties {
  public interface BasePropertyContainer {
    PropertyValue<S> Property<S>(DependencyProperty Property);
    PropertyValue<S> Property<S>(DependencyProperty ReadProperty, DependencyProperty WriteProperty);

    PropertyValue<S> Property<S>(DependencyProperty[] Properties, IntBl index);

  }
  public class PropertyContainer<T> : BasePropertyContainer where T : DependencyObject {
    private readonly BasePropertyContainer Underlying;
    public PropertyContainer(BasePropertyContainer Underlying) {
      this.Underlying = Underlying;
    }
    public PropertyValue<S> Property<S>(DependencyProperty Property) { return Underlying.Property<S>(Property); }
    public PropertyValue<S> Property<S>(DependencyProperty ReadProperty, DependencyProperty WriteProperty) { return Underlying.Property<S>(ReadProperty, WriteProperty); }

    public PropertyValue<S> Property<S>(DependencyProperty[] Properties, IntBl index) {
      return Underlying.Property<S>(Properties, index);
    }
    public static implicit operator PropertyContainer<T>(Expr<T> value) { return new PropertyContainer<T>(value); }
    public static implicit operator PropertyContainer<T>(Brand<T> value) { return new PropertyContainer<T>(value.Underlying); }
    public static implicit operator PropertyContainer<T>(T value) { return new PropertyContainer<T>(new Constant<T>(value)); }
  }
  public interface DependencyPropertyValue : BaseExpr {
    DependencyObject Target { get; }
    DependencyProperty ReadProperty { get; }
    DependencyProperty WriteProperty { get; }
  }
  public abstract class PropertyValue<T> : Expr<T> {}


  public abstract class DependencyPropertyValue<T> : PropertyValue<T>, DependencyPropertyValue, AssignNowExpr<T>, BindableExpr<T> {
    public abstract Expr BaseTargetValue { get; } 
    public abstract DependencyObject Target { get; }
    /// <summary>
    /// Dependency property used for reading.
    /// </summary>
    public DependencyProperty ReadProperty { get; internal set; }
    /// <summary>
    /// Dependency property used for writing.
    /// </summary>
    public DependencyProperty WriteProperty { get; internal set; }

    public abstract Action SetNow(Expr<T> value);
    public abstract Action<S> SetNow<S>(Expr<T> value, ParameterExpr<S> param);
    public abstract Action<S, U> SetNow<S, U>(Expr<T> value, ParameterExpr<S> paramA, ParameterExpr<U> paramB);
    public abstract Action<S, U, V> SetNow<S, U, V>(Expr<T> value, ParameterExpr<S> paramA, ParameterExpr<U> paramB, ParameterExpr<V> paramC);
    public abstract Action<S, U, V, W> SetNow<S, U, V, W>(Expr<T> value, ParameterExpr<S> paramA, ParameterExpr<U> paramB, ParameterExpr<V> paramC, ParameterExpr<W> paramD);
    public abstract bool SetNow(T value);
    public abstract bool BindWith(Expr<T> value);

  }

  /// <summary>
  /// Non-braded Bl value that is a wrapper around a WPF dependency object/property pair.
  /// </summary>
  /// <typeparam name="T"></typeparam>
  public class DependencyPropertyValue<S,T> : DependencyPropertyValue<T> {
    public Expr<S> TargetValue { get; internal set; }

    public override Expr BaseTargetValue {
      get { return TargetValue; }
    }

    /// <summary>
    /// Target dependency object.
    /// </summary>
    public override DependencyObject Target { get { return (DependencyObject) (object) TargetValue.CurrentValue; } }

    public DependencyPropertyValue() {}
    public override Eval<EVAL>.Info<T> Eval<EVAL>(Eval<EVAL> txt) {
      if (txt is DependencyPropertyEval<EVAL>) return ((DependencyPropertyEval<EVAL>)txt).PropertyValue(this);
      if (txt is ForceEval) {
        return (Eval<EVAL>.Info<T>)
          ((ForceEval)txt).Force<T>(() => (T)Target.GetValue(ReadProperty));
        // return (EvalVisitor<EVAL>.Info<T>)((BaseDefaultEval)txt).Force<T>((x) => (T)Target.GetValue(ReadProperty));
      } else if (txt is TranslateEval<EVAL>) {
        return ((TranslateEval<EVAL>)txt).NoTranslation(this);
      }
      throw new NotSupportedException();
    }
    public override int GetHashCode() {
      return TargetValue.GetHashCode() + ReadProperty.GetHashCode() + WriteProperty.GetHashCode();
    }
    public override bool Equals(object obj) {
      if (!(obj is DependencyPropertyValue<T>)) return false;
      var other = (DependencyPropertyValue<T>)obj;
      return other.BaseTargetValue == TargetValue && other.ReadProperty == ReadProperty && other.WriteProperty == WriteProperty;
    }
    public override string ToString() {
      return "" + ReadProperty.Name + " (" + Target.GetValue(ReadProperty) + ")";
    }
    public override bool BindWith(Expr<T> RHS) {
      return (new LinqBindEval()).Accept(this, RHS);
    }
    public override Action<K> SetNow<K>(Expr<T> RHS, ParameterExpr<K> arg) {
      var slots = new ParameterContext();
      slots.Add(arg);
      var eval = new DefaultLinqEval(slots);
      var RHS0 = ((DefaultLinqEval.MyInfo<T>)RHS.Eval(eval)).Value;
      return linq.Expression.Lambda<Action<K>>(RHS0, slots.Flush()).Compile();
    }
    public override Action<K,U> SetNow<K,U>(Expr<T> RHS, ParameterExpr<K> argA, ParameterExpr<U> argB) {
      var slots = new ParameterContext();
      slots.Add(argA);
      slots.Add(argB);
      // XXX: data binding!
      var eval = new DefaultLinqEval(slots);
      var RHS0 = ((DefaultLinqEval.MyInfo<T>)RHS.Eval(eval)).Value;
      var Target = ((DefaultLinqEval.MyInfo<S>)this.TargetValue.Eval(eval)).Value;
      Target = linq.Expression.Convert(Target, typeof(DependencyObject));
      

      var lambda = linq.Expression.Lambda<Action<K,U>>(RHS0, slots.Flush());
      return lambda.Compile();
    }
    public override Action<K, U, V> SetNow<K, U, V>(Expr<T> RHS, ParameterExpr<K> argA, ParameterExpr<U> argB, ParameterExpr<V> argC) {
      var slots = new ParameterContext();
      slots.Add(argA);
      slots.Add(argB);
      slots.Add(argC);
      var eval = new DefaultLinqEval(slots);
      var RHS0 = ((DefaultLinqEval.MyInfo<T>)RHS.Eval(eval)).Value;
      return linq.Expression.Lambda<Action<K, U, V>>(RHS0, slots.Flush()).Compile();
    }
    public override Action<K, U, V, W> SetNow<K, U, V, W>(Expr<T> RHS, ParameterExpr<K> argA, ParameterExpr<U> argB, ParameterExpr<V> argC, ParameterExpr<W> argD) {
      var slots = new ParameterContext();
      slots.Add(argA);
      slots.Add(argB);
      slots.Add(argC);
      slots.Add(argD);
      var eval = new DefaultLinqEval(slots);
      var RHS0 = ((DefaultLinqEval.MyInfo<T>)RHS.Eval(eval)).Value;
      return linq.Expression.Lambda<Action<K, U, V, W>>(RHS0, slots.Flush()).Compile();
    }
    
    public override bool SetNow(T RHS) {
      Target.SetValue(WriteProperty, RHS);
      return true;
    }

    public override Action SetNow(Expr<T> RHS) {
      var SetRHS = DefaultLinqEval.Eval<T>(RHS);
      return () =>
        Target.SetValue(WriteProperty, SetRHS());
    }
  }
  public interface DependencyPropertyEval<SELF> where SELF : Eval<SELF> {
    Eval<SELF>.Info<T> PropertyValue<T>(DependencyPropertyValue<T> value);
  }
  /*
  public interface DependencyPropertyEval2<SELF> : DependencyPropertyEval<SELF> where SELF : Eval<SELF> {
    Eval<SELF>.Info<S> PropertyValue<T, S>(Eval<SELF>.Info<T> container, DependencyProperty P) where T : DependencyObject;
  }
  */

  public class LinqBindEval : LinqEval<LinqBindEval>, Assign, DependencyPropertyEval<LinqBindEval> /*, DependencyPropertyEval<LinqBindEval>*/ {
    /* */
    public readonly linq.ParameterExpression Args;
    public LinqBindEval() : base(null) { 
      // no slots, manage the one argument ourselves!
      Args = linq.Expression.Parameter(typeof(object[]), "Arguments");
    }
    private LinqBindEval(LinqBindEval toCopy) : base(null) {
      Args = toCopy.Args;
      foreach (var b in toCopy.Binding.Bindings)
        Binding.Bindings.Add(b);
      foreach (var k in toCopy.Properties)
        Properties[k.Key] = k.Value;
      toCopy.DoTwoWay = DoTwoWay;
    }
    private readonly Dictionary<Expr, int> Properties = new Dictionary<Expr, int>();
    public struct BindKey {
      public linq.Expression Target;
      public DependencyProperty Property;
    }
    public readonly List<BindKey> BindKeys = new List<BindKey>();

    public readonly MultiBinding Binding = new MultiBinding();
    public bool DoTwoWay = false;
    public bool Delay = false;
    public Assign Copy() {
      return new LinqBindEval(this);
    }
    public override Info<T> PropertyValue<T>(DependencyPropertyValue<T> value) {
      // be conservative!

      if (!Properties.ContainsKey(value)) {
        int index;
        if (Delay) {
          index = BindKeys.Count;
          BindKeys.Add(new BindKey() {
            Target = ((MyInfo)value.BaseTargetValue.BaseEval(this)).BaseValue, Property = value.ReadProperty,
          });
        } else {
           index = Binding.Bindings.Count;
          Binding.Bindings.Add(new Binding() { Source = value.Target, Path = new PropertyPath(value.ReadProperty) });
        }
        Properties[value] = index;
      }
      var access = linq.Expression.ArrayAccess(Args, linq.Expression.Constant(Properties[value]));
      var convert = linq.Expression.Convert(access, typeof(T));
      return new MyInfo<T>() { Value = convert };
    }
    public override Info<S> PropertyValue<R, S>(Info<R> container, DependencyProperty P) {
      var container0 = ((MyInfo<R>)container).Value;
      if (container0 is linq.ConstantExpression) {
        DependencyObject container1 = (DependencyObject)((linq.ConstantExpression)container0).Value;
        return (new DependencyPropertyValue<DependencyObject,S>() { TargetValue = container1, ReadProperty = P, WriteProperty = P }).Eval(this);
      } else if (Delay) {


      }
      throw new NotSupportedException();
    }
    public bool Accept<T>(Expr<T> LHS, Expr<T> RHS) {
      if (!(LHS is DependencyPropertyValue<T>)) return false;
      var LHS0 = (DependencyPropertyValue<T>)LHS;
      var RHS0 = ((MyInfo<T>)RHS.Eval(this)).Value;
      var body = this.PopScope<T>(RHS0);

      var F0 = linq.Expression.Lambda<Microsoft.Func<object[], T>>(body, Args).Compile();
      Func<object[], T> F = (args) => F0(args);
      if (DoTwoWay) throw new NotImplementedException();
      Binding.Mode = BindingMode.OneWay;
      Binding.Converter = new BindConverter<T>() { F = F, G = null };
      var e = BindingOperations.SetBinding(LHS0.Target, LHS0.WriteProperty, Binding);
      return true;
    }
  }
  public class BindEval : Eval<BindEval>, DependencyPropertyEval<BindEval>, Assign {
    public readonly MultiBinding Binding = new MultiBinding();
    public bool DoTwoWay = false;
    public Assign Copy() {
      var ret = new BindEval();
      foreach (var b in Binding.Bindings) 
        ret.Binding.Bindings.Add(b);
      ret.DoTwoWay = DoTwoWay;
      return ret;
    }
    public bool Accept<T>(Expr<T> LHS, Expr<T> RHS) {
      if (!(LHS is DependencyPropertyValue<T>)) return false;
      var LHS0 = (DependencyPropertyValue<T>)LHS;
      var F = ((BindEval.MyInfo<T>) RHS.Eval(this)).F;
      // compute G
      //RHS.Solve<T>(null, LHS);
      Func<T, object[]> G = null;

      if (DoTwoWay) {
        var twoWay = new TwoWayAssign<T>() { Value = LHS0 };
        if (!RHS.Solve(twoWay, LHS0)) throw new NotSupportedException();
        var target = ((Binding)Binding.Bindings[0]);
        (target.Source == twoWay.Found.Target).Assert();
        (target.Path.PathParameters[0] == twoWay.Found.ReadProperty).Assert();
        Binding.Bindings.Add(new Binding() { Source = twoWay.Found.Target, Path = new PropertyPath(twoWay.Found.WriteProperty) });
        G = (x) => {
          var ret = new object[Binding.Bindings.Count];
          ret[Binding.Bindings.Count - 1] = twoWay.G(x);
          for (int i = 0; i < Binding.Bindings.Count - 1; i++) {
            var target0 = ((Binding)Binding.Bindings[i]);
            ret[i] = System.Windows.Data.Binding.DoNothing; //  
            // ((DependencyObject)target0.Source).GetValue((DependencyProperty)target0.Path.PathParameters[0]);
          }
          return ret;
        };
        Binding.Mode = BindingMode.TwoWay;
      } else {
        Binding.Mode = BindingMode.OneWay;
      }
      Binding.Converter = new BindConverter<T>() { F = F, G = G };
      var e = BindingOperations.SetBinding(LHS0.Target, LHS0.WriteProperty, Binding);
      //e.UpdateTarget();
      //e.Status == BindingStatus.Active;
      return true;
    }

    public class MyInfo<T> : Info<T> {
      public Func<object[],T> F;
    }
    public override Eval<BindEval>.Info<S> UpCast<T, S>(Eval<BindEval>.Info<T> info) {
      return new MyInfo<S>() {
        F = args => ((MyInfo<T>) info).F(args),
      };
    }

    public override Eval<BindEval>.Info<T> Constant<T>(Constant<T> constant) {
      return new MyInfo<T>() { F = (args) => constant.Value };
    }
    public override Eval<BindEval>.Info<T> Operate<S, T>(Operation<S, T> op) {
      var F = ((MyInfo<S>) op.Value.Eval(this)).F;
      return new MyInfo<T>() { F = (args) => op.Op.Apply(F(args)) };
    }
    public override Eval<BindEval>.Info<U> Operate<S, T, U>(Operation<S, T, U> op) {
      var F1 = ((MyInfo<S>)op.Value1.Eval(this)).F;
      var F2 = ((MyInfo<T>)op.Value2.Eval(this)).F;
      return new MyInfo<U>() { F = (args) => op.Op.Apply(F1(args), F2(args)) };
    }
    public override Eval<BindEval>.Info<V> Operate<S, T, U, V>(Operation<S, T, U, V> op) {
      var F1 = ((MyInfo<S>)op.Value1.Eval(this)).F;
      var F2 = ((MyInfo<T>)op.Value2.Eval(this)).F;
      var F3 = ((MyInfo<U>)op.Value3.Eval(this)).F;
      return new MyInfo<V>() { F = (args) => op.Op.Apply(F1(args), F2(args), F3(args)) };
    }
    public override Eval<BindEval>.Info<T> ArityX<T, ARITY>(ArityInfo<T, ARITY>.ArityExpr value) {
      var FS = new Func<object[], double>[value.Values.Count];
      for (int i = 0; i < FS.Length; i++)
        FS[i] = ((MyInfo<double>)value.Values[i].Eval(this)).F;
      return new MyInfo<T>() {
        F = (args) => {
          var DS = new double[FS.Length];
          for (int i = 0; i < FS.Length; i++)
            DS[i] = FS[i](args);
          return ArityInfo<T, ARITY>.Instance.ToT(DS);
        }
      };
    }
    public Info<T> PropertyValue<T>(DependencyPropertyValue<T> value) {
      if (!(value is DependencyPropertyValue<T>)) throw new NotSupportedException();
      var value0 = (DependencyPropertyValue<T>)value;
      var index = Binding.Bindings.Count;
      Binding.Bindings.Add(new Binding() { Source = value0.Target, Path = new PropertyPath(value0.ReadProperty) });
      return new MyInfo<T>() { F = (args) => (T)args[index] };
    }

  }
  public class BindConverter<T> : IMultiValueConverter {
    public Func<object[], T> F;
    public Func<T, object[]> G;
    public object Convert(object[] args, Type type, object whatever, System.Globalization.CultureInfo useless) {
      return F(args);
    }
    public object[] ConvertBack(object result, Type[] argTypes, object whatever, System.Globalization.CultureInfo useless) {
      if (G == null) throw new NotSupportedException();
      return G((T) result);
    }
  }

  public class TwoWayAssign<T> : ApplyEval<T>, Assign, DependencyPropertyEval<ApplyEval<T>> {
    public PropertyValue<T> Value;
    public Func<T, object> G;
    public DependencyPropertyValue Found;
    public ApplyEval<T> Eval { get { return this; } }
    public Assign Copy() { return this; }
    public bool Accept<S>(Expr<S> LHS, Expr<S> RHS) {
      if (!(LHS is DependencyPropertyValue<S>)) return false;
      Found = ((DependencyPropertyValue<S>) LHS);
      this.G = x => ((MyInfo<S>)RHS.Eval(this)).Value(x);
      return true;
    }
    public Info<S> PropertyValue<S>(DependencyPropertyValue<S> value) {
      if (!(value is DependencyPropertyValue<S>)) throw new NotSupportedException();
      var value0 = (DependencyPropertyValue<S>)value;
      if (value.Equals(Value)) {
        return new MyInfo<S>() {
          Value = (x) => (S)(object) x
        };
      } else {
        return new MyInfo<S>() {
          Value = (x) =>
            (S)value0.Target.GetValue(value0.ReadProperty)
        };
      }
    }
  }
  public class AnimateToAssign2 : Assign {
    public Func<AnimationTimeline, AnimationTimeline> F = t => t;
    public Func<DoubleBl, DoubleBl> Tween = null;
    public Assign Copy() { return new AnimateToAssign2() { F = F, Tween = Tween }; }
    public bool Accept<T>(Expr<T> LHS, Expr<T> RHS) {
      if (!(LHS is DependencyPropertyValue<T>)) return false;
      var LHS0 = (DependencyPropertyValue<T>)LHS;
      var branded = Brand.Brands[typeof(T)](LHS);
      if (!(branded is BaseCoreValue<T>)) return false;
      var branded0 = (BaseCoreValue<T>)branded;
      var ani = branded0.MakeAnimation(Tween);
      ani = (BaseCustomAnimation) F(ani);
      ani.To0 = RHS;
      ani.From0 = LHS;
      ((IAnimatable)LHS0.Target).BeginAnimation(LHS0.WriteProperty, ani);
      return true;
    }
  }
  public abstract class BaseKeyPair {
    public abstract void Animate(double[] percents, Func<AnimationTimeline, AnimationTimeline> F);

  }
  public class KeyPair<T> : BaseKeyPair {
    public DependencyPropertyValue<T> Target;
    public readonly List<T> Values = new List<T>();
    public override void Animate(double[] percents, Func<AnimationTimeline,AnimationTimeline> F) {
      AnimationTimeline a;
      if (typeof(T) == typeof(double)) {
        var b = new DoubleAnimationUsingKeyFrames();
        for (int i = 0; i < Values.Count; i++) {
          b.KeyFrames.Add(new LinearDoubleKeyFrame((double)(object)Values[i], KeyTime.FromPercent(percents[i])));
        }
        a = b;
      } else if (typeof(T) == typeof(Point)) {
        var b = new PointAnimationUsingKeyFrames();
        for (int i = 0; i < Values.Count; i++) {
          b.KeyFrames.Add(new LinearPointKeyFrame((Point)(object)Values[i], KeyTime.FromPercent(percents[i])));
        }
        a = b;
      } else if (typeof(T) == typeof(Color)) {
        var b = new ColorAnimationUsingKeyFrames();
        for (int i = 0; i < Values.Count; i++) {
          b.KeyFrames.Add(new LinearColorKeyFrame((Color)(object)Values[i], KeyTime.FromPercent(percents[i])));
        }
        a = b;
      } else if (typeof(T) == typeof(bool)) {
        var b = new BooleanAnimationUsingKeyFrames();
        for (int i = 0; i < Values.Count; i++) {
          b.KeyFrames.Add(new DiscreteBooleanKeyFrame((bool)(object)Values[i], KeyTime.FromPercent(percents[i])));
        }
        a = b;
      } else if (typeof(T) == typeof(int)) {
        var b = new Int32AnimationUsingKeyFrames();
        for (int i = 0; i < Values.Count; i++) {
          b.KeyFrames.Add(new DiscreteInt32KeyFrame((int)(object)Values[i], KeyTime.FromPercent(percents[i])));
        }
        a = b;
      } else throw new NotImplementedException();
      ((IAnimatable)Target.Target).BeginAnimation(Target.WriteProperty, F(a));
    }
  }
  public class AnimateKeyAssign : Assign {
    public readonly ApplyEval<object> Eval0 = new ApplyEval<object>();
    public ApplyEval<object> Eval { get { return Eval0; } }
    public Assign Copy() { return this; }
    public readonly Dictionary<Expr, BaseKeyPair> Record = new Dictionary<Expr, BaseKeyPair>();
    public bool Accept<T>(Expr<T> LHS, Expr<T> RHS) {
      if (!(LHS is DependencyPropertyValue<T>)) return false;
      var LHS0 = (DependencyPropertyValue<T>)LHS;
      var Value = ((ApplyEval<object>.MyInfo<T>)RHS.Eval(Eval)).Value(null);
      if (!Record.ContainsKey(LHS0)) Record[LHS0] = new KeyPair<T>() { Target = LHS0 };
      ((KeyPair<T>)Record[LHS0]).Values.Add(Value);
      return true;
    }
  }
  /// <summary>
  /// Auxillary class use to configure and execute animations.
  /// </summary>
  public class Animate<T, BRAND> where BRAND : Brand<T, BRAND> {
    internal BRAND Target;
    Func<AnimationTimeline, AnimationTimeline> F = t => t;
    Func<DoubleBl, DoubleBl> Tween0 = null;
    bool DoStopOnCompleted = true;
    /// <summary>
    /// Specify duration of animation in milliseconds.
    /// </summary>
    /// <param name="millis">The timeline's simple duration in milliseconds: the amount of time this timeline takes to
    ///     complete a single forward iteration</param>
    /// <returns>Itself.</returns>
    public Animate<T, BRAND> Duration(double millis) {
      var G = F;
      F = t => G(t).Duration(millis);
      return this;
    }
    /// <summary>
    /// Specify a callback to be called when the animation is completed.
    /// </summary>
    /// <param name="P">Callback called when the animation is done. Argument will be the animated property.</param>
    /// <returns>Itself.</returns>
    public Animate<T, BRAND> Completed(Action<BRAND> P) {
      var G = F;
      F = t => {
        t = G(t);
        t.Completed += (x, y) => P(Target);
        return t;
      };
      return this;
    }
    /// <summary>
    /// If true, stop and capture the value of this animation once it is completed. 
    /// This means that future local updates can affect the value. By default, stop 
    /// on completed is true for an animation, set to false if this is not desired behavior.
    /// </summary>
    /// <returns>Itself.</returns>
    public Animate<T, BRAND> StopOnCompleted(bool b) {
      DoStopOnCompleted = b;
      return this;
    }


    /// <summary>
    /// Specify duration of animation as a time span.
    /// </summary>
    /// <param name="millis">The timeline's simple duration: the amount of time this timeline takes to
    ///     complete a single forward iteration</param>
    /// <returns>Itself.</returns>
    public Animate<T, BRAND> Duration(TimeSpan span) {
      var G = F;
      F = t => G(t).Duration(span);
      return this;
    }
    /// <summary>
    /// Specify duration of animation as a time span.
    /// </summary>
    /// <param name="millis">The timeline's duration: the amount of time this timeline takes to
    ///     complete a single forward iteration</param>
    /// <returns>Itself.</returns>
    public Animate<T, BRAND> Duration(Duration duration) {
      var G = F;
      F = t => G(t).Duration(duration);
      return this;
    }
    /// <summary>
    /// Sets the time at which this System.Windows.Media.Animation.Timeline
    /// should begin
    /// </summary>
    /// <param name="millis">The time (in millseconds) at which this System.Windows.Media.Animation.Timeline should begin,
    ///     relative to its parent's System.Windows.Media.Animation.Timeline.BeginTime.
    ///     If this timeline is a root timeline, the time is relative to its interactive
    ///     begin time (the moment at which the timeline was triggered). This value may
    ///     be positive, negative.
    ///     The default value is zero</param>
    public Animate<T, BRAND> Delay(double millis) {
      var G = F;
      F = t => G(t).BeginTime(millis);
      return this;
    }
    /// <summary>
    /// Sets the time at which this System.Windows.Media.Animation.Timeline
    /// should begin
    /// </summary>
    /// <param name="span">The time at which this System.Windows.Media.Animation.Timeline should begin,
    ///     relative to its parent's System.Windows.Media.Animation.Timeline.BeginTime.
    ///     If this timeline is a root timeline, the time is relative to its interactive
    ///     begin time (the moment at which the timeline was triggered). This value may
    ///     be positive, negative.
    ///     The default value is zero</param>
    /// <returns></returns>
    public Animate<T, BRAND> Delay(TimeSpan span) {
      var G = F;
      F = t => G(t).BeginTime(span);
      return this;
    }


    /// <summary>
    /// Specify that animation should run forever.
    /// </summary>
    /// <returns>Itself.</returns>
    public Animate<T, BRAND> Forever() {
      var G = F;
      F = t => G(t).Forever();
      return this;
    }
    /// <summary>
    /// Specify that animation should reverse after completing.
    /// </summary>
    /// <returns>Itself.</returns>
    public Animate<T, BRAND> AutoReverse() {
      var G = F;
      F = t => G(t).AutoReverse();
      return this;
    }
    public Animate<T, BRAND> Tween(Func<DoubleBl, DoubleBl> Tween) {
      (Tween0 == null).Assert();
      Tween0 = Tween;
      return this;
    }

    /// <summary>
    /// Start destination-based animation to specified value. 
    /// </summary>
    public BRAND To {
      set {
        if (Percents0 != null) throw new NotSupportedException();
        if (DoStopOnCompleted) this.Completed(p => p.Stop(true));
        if (!Target.Underlying.Solve(new AnimateToAssign2() { F = F, Tween = Tween0 }, value)) throw new NotSupportedException();
      }
    }
    private DoubleBl[] Percents0;
    /// <summary>
    /// Set percentages of key frame animation.
    /// </summary>
    /// <param name="Percents">List of percents.</param>
    /// <returns>Itself.</returns>
    public Animate<T, BRAND> Percents(params DoubleBl[] Percents) {
      Percents0 = Percents;
      return this;
    }

    /// <summary>
    /// Start key frame animation according to percents previously specified.
    /// </summary>
    /// <param name="values">List of key values corresponding to previously specified percents.</param>
    public void Keys(params BRAND[] values) {
      if (Tween0 != null) throw new NotSupportedException();
      if (Percents0.Length != values.Length) throw new ArgumentException();
      if (DoStopOnCompleted) this.Completed(p => p.Stop(true));
      var a = new AnimateKeyAssign() { };
      double[] percents = new double[Percents0.Length];
      for (int i = 0; i < Percents0.Length; i++) {
        Target.Underlying.Solve(a, values[i]);
        percents[i] = Percents0[i].CurrentValue;
      }
      foreach (var r in a.Record) {
        r.Value.Animate(percents, F);
      }
    }
  }
  public class GetProperty<S, T> where S : DependencyObject {
    public DependencyProperty Property { get; internal set; }
    public PropertyValue<T> this[PropertyContainer<S> Container] {
      get { return Container.Property<T>(Property); }
    }
  }


  //public delegate PropertyValue<T> GetProperty<S,T>(PropertyContainer<S> Container) where S : DependencyObject;

  public class GetProperties<S, T> where S : DependencyObject {
    internal DependencyProperty[] Properties;
    public int Count { get { return Properties.Length; } }
    public PropertyValue<T> this[PropertyContainer<S> Container, IntBl idx] {
      get { return Container.Property<T>(Properties, idx); }
      set { this[Container, idx].Bind = value; }
    }
    public GetProperty<S,T> this[int idx] {
      get { return new GetProperty<S, T>() { Property = Properties[idx] }; }
    }
    public PropertyValues<S, T> this[PropertyContainer<S> Container] {
      get { return new PropertyValues<S, T>() { Container = Container, Properties = Properties }; }
    }
  }
  //public delegate PropertyValues<S,T> GetProperties<S, T>(PropertyContainer<S> Container) where S : DependencyObject;
  public class PropertyValues<S,T> where S : DependencyObject {
    internal DependencyProperty[] Properties;
    internal PropertyContainer<S> Container;
    public PropertyValue<T> this[IntBl idx] {
      get { return Container.Property<T>(Properties, idx); }
      set { this[idx].Bind = value; }
    }
    public PropertyValue<T> this[int idx] {
      get { return Container.Property<T>(Properties[idx]); }
      set { this[idx].Bind = value; }
    }
  }
  public static partial class DataBindingExtensions {
    /// <summary>
    /// Create a WPF dependency property Bl value.
    /// </summary>
    /// <typeparam name="T">Type of core value.</typeparam>
    /// <returns>Bl value.</returns>
    
    public static PropertyValue<T> Bl<T>(this DependencyObject Target, DependencyProperty Property) {
      return new DependencyPropertyValue<DependencyObject, T>() {
        TargetValue = Target, ReadProperty = Property, WriteProperty = Property,
      };
    }

    /// <summary>
    /// Create a WPF dependency property Bl value.
    /// </summary>
    /// <typeparam name="T">Type of core value.</typeparam>
    /// <param name="ReadProperty">Property used for reading.</param>
    /// <param name="WriteProperty">Property used for writing.</param>
    /// <returns></returns>

    public static PropertyValue<T> Bl<T>(this DependencyObject Target, DependencyProperty ReadProperty, DependencyProperty WriteProperty) {
      return new DependencyPropertyValue<DependencyObject,T>() {
        TargetValue = Target, ReadProperty = ReadProperty, WriteProperty = WriteProperty,
      };
    }
    public static GetProperty<OWNER, TYPE> NewProperty<OWNER, TYPE>(this string name, TYPE defaultValue, TypedDependencyPropertyChangedEventHandler<OWNER, TYPE> update, Func<OWNER,TYPE,TYPE> coerce) where OWNER : DependencyObject {
      DependencyProperty dp = null;
      dp = DependencyProperty.Register(name, typeof(TYPE), typeof(OWNER), new PropertyMetadata(defaultValue, (x, y) => {
        if (y.Property == dp) update((OWNER)x, (TYPE)y.OldValue, (TYPE)y.NewValue);
      }, (self, value) => coerce((OWNER) self, (TYPE) value)));
      return new GetProperty<OWNER, TYPE>() { Property = dp };
    }
    public static GetProperty<OWNER, TYPE> NewProperty<OWNER, TYPE>(this string name, TYPE defaultValue, TypedDependencyPropertyChangedEventHandler<OWNER, TYPE> update) where OWNER : DependencyObject {
      DependencyProperty dp = null;
      dp = DependencyProperty.Register(name, typeof(TYPE), typeof(OWNER), new PropertyMetadata(defaultValue, (x, y) => {
        if (y.Property == dp) update((OWNER)x, (TYPE)y.OldValue, (TYPE)y.NewValue);
      }));
      return new GetProperty<OWNER, TYPE>() { Property = dp };
    }
    public static GetProperty<OWNER, TYPE> NewProperty<OWNER, TYPE>(this string name, TYPE defaultValue) where OWNER : DependencyObject {
      var dp = DependencyProperty.Register(name, typeof(TYPE), typeof(OWNER), new PropertyMetadata(defaultValue));
      return new GetProperty<OWNER, TYPE>() { Property = dp };
    }
    public static GetProperty<OWNER, TYPE> NewProperty<OWNER, TYPE>(this string name) where OWNER : DependencyObject {
      var dp = DependencyProperty.Register(name, typeof(TYPE), typeof(OWNER));
      return new GetProperty<OWNER, TYPE>() { Property = dp };
    }
    public static GetProperties<OWNER, TYPE> NewProperties<OWNER, TYPE>(this string name, int count) where OWNER : DependencyObject {
      var Properties = new DependencyProperty[count];
      for (int i = 0; i < count; i++) {
        Properties[i] = DependencyProperty.Register(name + "_" + i, typeof(TYPE), typeof(OWNER));
      }
      return new GetProperties<OWNER, TYPE>() {
        Properties = Properties,
      };
    }


    public static Func<PropertyContainer<OWNER>, Expr<Point>> NewProperty<OWNER>(this string name, Point defaultValue) where OWNER : DependencyObject {
      var x = (name + "X").NewProperty<OWNER, double>(defaultValue.X);
      var y = (name + "Y").NewProperty<OWNER, double>(defaultValue.Y);
      return owner => new PointBl(x[owner], y[owner]).Underlying;
    }
  }

}
